Introduction

This is an notebook to show the process I usually take to analyze data in order to solve some questions related to database analysis.

The source of the database is from Kaggle. The data is a relational database of sqlite and it have 7 tables with information of 8 years of football seasons (2008-2015) of 11 European countries.

This code was made in Rstudio and has some querys from SLQ integrated in order to show my abilities in both languages. This works because the source of data is an sql file but it can be imported to Rstudio.

For this project, I want to answer this questions related to the data: 1. ¿What was the average age of football players in Europe in 2008 and 2016? 2. ¿Who was the best player in Europe according to the overall ranking? 3. What team score the most goals in Europe between 2008 and 2016? 4. ¿What was the team that had the most points in Europe and what teams were the best home or away?

Import data

The first step I take in the analysis process is to get to know the database.

Let´s start charging the packages we need:

After downloading the data, we need to import it to Rstudio. First, we need to establish a connection between Rstudio and the SQL database

bd_football <- dbConnect(SQLite(), dbname="database.sqlite")

The package RSQLite includes different functions to manipulate SQL with querys in Rstudio. We see the names of the tables that the database includes

print(bd_names)
[1] "Country"           "League"            "Match"             "Player"           
[5] "Player_Attributes" "Team"              "Team_Attributes"   "sqlite_sequence"  

With that code we can see all names in the database, so we can select the table Player and see more of it.We need to use SQL code to get the table to Rstudio:

players <- #save the name of the table
  as_tibble( #Convert to tibble
    dbGetQuery(bd_football, 
               "Select* from Player")) %>% 
  clean_names()

players #Print the tibble players

Now, we can start to answer the above questions

1.What was the average age of football players in Europe in 2008 and 2016?

We saw that the table Players had the information needed to answe this first question. There are to ways we can do it. The first is with SQL:

as_tibble(dbGetQuery(bd_football, 
                     "Select 
                    avg(strftime('%Y', '2008-12-12 00:00:00') - 
                     strftime('%Y', birthday))  as average_age_2008,
                     avg(strftime('%Y', '2016-12-12 00:00:00') - 
                     strftime('%Y', birthday)) as average_age_2016
                     From Player"))

That SQL query helped us to extract the year of the date of birth of each player and then we rest it to 2008 and 2016 so we could get the average. However, we can also do this in R:

players %>% 
  select(birthday) %>% 
  mutate(birthday = as_date(birthday),
         year_of_birth = year(birthday),
         age_2008 = 2008 - year_of_birth,
         age_2016 = 2016 - year_of_birth) %>% 
  summarise(avg_age_2008 = mean(age_2008),
            avg_age_2016 = mean(age_2016))

So, we have the average of age but we can do a little bit more to explore the data. For example, lets include a graph of the relation of height and weight of the players.


players %>% 
  mutate(birthday = as_date(birthday),
         year_of_birth = year(birthday),
         age_2008 = 2008 - year_of_birth) %>% #Obtain the age of the players in 2008
  mutate(group_age = if_else(age_2008 <21, #New variable to get the group of age
                             "Less than 21",
                             if_else(age_2008 <26, 
                                     "Less than 26",
                                     if_else(age_2008 <26, 
                                             "Less than 26",
                                             if_else(age_2008 <30, 
                                                     "Less than 30",
                                                     "More than 30")))
                             
                             )) %>% 
  ggplot(aes(weight, height, col = group_age))+ #Start the graph
  geom_jitter(width = 1.5)+ #Add some noise to the points
  guides(colour = guide_legend(override.aes = list(size=3)))+ #Make the point in the legend bigger
  labs(title = "Relation Height and Weight",
       subtitle = "European Football Players in 2008",
       x = "W (LB)",
       y = "H (CM)",
       col = "Age Group")+
  scale_color_manual(values = c("#440154", #Make the color scale
                                "#3b528b", 
                                "#21918c",
                                "#fde725"))+
  scale_x_continuous(breaks = c(120, 150, 175,200, 220, 240))+ #Make the breaks
  theme(plot.title = element_text(hjust = .5),
        plot.subtitle = element_text(hjust = .5),
        legend.background = element_blank(),
        legend.key = element_blank()) #Adjust some elements

2. Who was the best player in Europe according to the overall ranking?

First, lets get the data with SQL. We can get data from the table Player such as the height, weight and name. With the table Player_Attributes, we can find the overall rating in every season so we can do an average. With this simple SQL code we get the data from both tables and then create a new variable with Tidyverse.

(rating_height <- as_tibble(dbGetQuery(bd_football, 
                     "Select b.player_name, 
                     b.height, 
                     b.weight,
                     sum(a.overall_rating) as overall_rating, 
                     avg(overall_rating) as average_rating
                     from Player_Attributes as a
                     Left Join Player as b
                     On b.player_api_id = a.player_api_id
                     Group by a.player_api_id
                     Order by average_rating desc")) %>% 
  mutate(weight = weight * .454,
         weight_group = if_else(weight<=60, "Less than 60kg",
                                if_else(weight<=70, "Less than 70kg",
                                        if_else(weight <= 80, "Less than 75kg",
                                                "More than 80kg")))))

We found that Lionel Messi was the best ranked player with a overall rating in Europe between 2008 and 2016. But we can use the tibble created above to graph the distribution of rating and height to see the relation.

rating_height %>% 
  ggplot(aes(height, average_rating))+
  geom_jitter(aes(col = weight_group),
              size = 2.5,
              width = 1.5,
              height = 1.5)+
  geom_text(data = rating_height %>% 
              filter(average_rating == max(rating_height$average_rating)|
                       average_rating == max(rating_height$average_rating[rating_height$average_rating!=max(rating_height$average_rating)])),
             aes(label = player_name),
            check_overlap = T, 
            nudge_x = 0.16,
            hjust = 0)+
  scale_color_manual(values = c("#fde725",
                                "#21918c", 
                                "#3b528b",
                                "#440154"))+
  guides(colour = guide_legend(override.aes = list(size=4)))+
  labs(title = "Average Overall Ranking and Height in European Footballers",
       subtitle = "2008 - 2016",
       color = "Weight Group",
       x = "Height",
       y = "Average Overall Rating")+
  theme(plot.title = element_text(hjust = .5),
        plot.subtitle = element_text(hjust = .5),
        legend.background = element_blank(),
        legend.key = element_blank())

3.What team score the most goals in Europe between 2008 and 2016?

Lets answer this question with a simple SQL code. We need to use two tables, the Team and Match so we can have the complete name of the team and sum the away and home goals of each team so we can get the top scorers.

as_tibble(dbGetQuery(bd_football, 
                     "SELECT
           b.team_long_name as team,
           sum(a.home_team_goal)+ sum(a.away_team_goal) as goals
           FROM Match as a
           Left Join Team as b on b.team_api_id = a.home_team_api_id 
           Group by b.team_api_id
           Order by goals Desc
           "))

Lets see the distrution of goals scored by european teams:

To answer the next questions, lets start by creating a new table with the data we need. Now, we know the information of the tables Match and Team, so we can get the information of each match played in every season in all countries. Also, based on the goals that each team scored we can make the points each one got. If you are not familiar with football, when a team wins they get 3 points, 1 for a tie and 0 for losing. The next SQL code creates that table called points:


(points <- as_tibble(dbGetQuery(bd_football, 
                     "SELECT
           b.team_long_name as home_team,
           a.home_team_goal as home_goals,
           (Case
           When a.home_team_goal = a.away_team_goal Then 1
           When a.home_team_goal > a.away_team_goal Then 3
           When a.home_team_goal < a.away_team_goal Then 0
           End) as home_point,
           c.team_long_name as away_team,
           a.away_team_goal as away_goals,
           (Case
           When a.home_team_goal = a.away_team_goal Then 1
           When a.home_team_goal > a.away_team_goal Then 0
           When a.home_team_goal < a.away_team_goal Then 3
           End) as away_point,
           a.date,
           a.season,
           d.name as league_name,
           e.name as country
           FROM Match as a
           Left Join Team as b on b.team_api_id = a.home_team_api_id 
           Left Join Team as c on c.team_api_id = a.away_team_api_id
           Left Join League as d on d.country_id = a.country_id
           Left Join Country as e on e.id = a.country_id
           "))) 

4. What was the team that had the most points in Europe and what teams were the best home and away team?

Let´s see how can we get the information with a more complex SQL query with subquerys and joins to get the variables we want:

as_tibble(dbGetQuery(bd_football,
                     "
              Select
                home.home_team as team,
                home.total_home_points as total_home_points,
                away.total_away_points as total_away_points
              From
                     (
                     Select
                     z.id_home as id_home,
                     z.home_team,
                     sum(z.home_point) as total_home_points
                     From
                     (Select
                     b.team_long_name as home_team,
                     c.team_long_name as away_team,
                     a.home_team_api_id as id_home,
                     (Case
                     When a.home_team_goal = a.away_team_goal Then 1
                     When a.home_team_goal > a.away_team_goal Then 3
                     When a.home_team_goal < a.away_team_goal Then 0
                     End) as home_point,
                     (Case
                     When a.home_team_goal = a.away_team_goal Then 1
                     When a.home_team_goal > a.away_team_goal Then 0
                     When a.home_team_goal < a.away_team_goal Then 3
                     End) as away_point,
                     a.away_team_api_id as id_away
                     FROM Match as a
                     Left Join Team as b on b.team_api_id = a.home_team_api_id
                     Left Join Team as c on c.team_api_id = a.away_team_api_id) as z
                     Group by id_home) as home
              Inner Join (
                          Select
                          z.id_away as id_away,
                          z.away_team,
                          sum(z.away_point) as total_away_points
                           From
                          (Select
                          b.team_long_name as home_team,
                          c.team_long_name as away_team,
                          a.home_team_api_id as id_home,
                          (Case
                          When a.home_team_goal = a.away_team_goal Then 1
                          When a.home_team_goal > a.away_team_goal Then 3
                          When a.home_team_goal < a.away_team_goal Then 0
                          End) as home_point,
                          (Case
                          When a.home_team_goal = a.away_team_goal Then 1
                          When a.home_team_goal > a.away_team_goal Then 0
                          When a.home_team_goal < a.away_team_goal Then 3
                          End) as away_point,
                          a.away_team_api_id as id_away
                          FROM Match as a
                          Left Join Team as b on b.team_api_id = a.home_team_api_id
                          Left Join Team as c on c.team_api_id = a.away_team_api_id) as z
                          Group by id_away) as away
              On away.away_team = home.home_team
              Order by total_home_points desc"
  
))

Now, the same process but in Tidyverse and a graph to visualize the data:

points_home <- points %>% 
  group_by(home_team) %>% 
  summarise(sum_home_points = sum(home_point)) %>% 
  rename(team = "home_team") %>% 
  arrange(-sum_home_points) %>% 
  filter(sum_home_points > 330)

points_away <- points %>% 
  group_by(away_team) %>% 
  summarise(sum_away_points = sum(away_point)) %>% 
  rename(team = "away_team") %>% 
  arrange(-sum_away_points) %>% 
  filter(sum_away_points >250)


home_away_points <- inner_join(points_home, points_away, by = join_by(team)) %>% 
  pivot_longer(-team, 
               names_to = "away_or_home",
               values_to = "points") %>%
  mutate(away_or_home = if_else(away_or_home == "sum_home_points",
                        "Home points",
                        "Away points")) %>% 
  ggplot(aes(fct_reorder(team, points), points))+
  geom_col(aes(fill = away_or_home), 
           position = position_dodge(width = .9),
           col = "black")+
  coord_flip()+
  scale_y_continuous(expand = c(0, 0),
                     limits = c(0, 420))+
  scale_fill_manual(values = c("#B12A90FF", 
                                "#0D0887FF"))+
  labs(title = "Home and Away Points in Europe",
       subtitle = "2008-2016",
       fill = "",
       x = "Team", 
       y = "Points")+
  theme_bw()+
  theme(panel.grid = element_line(linetype = 3,
                                  color = "black"),
        panel.grid.minor = element_blank(),
        axis.text = element_text(size = 12),
        legend.spacing.y = unit(.2, "cm"),
        legend.title = element_text(hjust = .5),
        legend.position = "bottom",
        plot.title = element_text(hjust = .5,
                                  face = "bold"),
        plot.subtitle = element_text(hjust = .5,
                                     face = "bold"))

ggplotly(home_away_points, width = 1000)
NA
LS0tDQp0aXRsZTogIkZvb3RiYWxsIEFuYWx5c2lzIg0KYXV0aG9yOiAiSGVsaW9zIEdhcmNpYSINCmRhdGU6ICJNYXkgMjAyMyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIEludHJvZHVjdGlvbg0KDQpUaGlzIGlzIGFuIG5vdGVib29rIHRvIHNob3cgdGhlIHByb2Nlc3MgSSB1c3VhbGx5IHRha2UgdG8gYW5hbHl6ZSBkYXRhIGluIG9yZGVyIHRvIHNvbHZlIHNvbWUgcXVlc3Rpb25zIHJlbGF0ZWQgdG8gZGF0YWJhc2UgYW5hbHlzaXMuDQoNClRoZSBzb3VyY2Ugb2YgdGhlIGRhdGFiYXNlIGlzIGZyb20gW0thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9odWdvbWF0aGllbi9zb2NjZXIpLiBUaGUgZGF0YSBpcyBhIHJlbGF0aW9uYWwgZGF0YWJhc2Ugb2Ygc3FsaXRlIGFuZCBpdCBoYXZlIDcgdGFibGVzIHdpdGggaW5mb3JtYXRpb24gb2YgOCB5ZWFycyBvZiBmb290YmFsbCBzZWFzb25zICgyMDA4LTIwMTUpIG9mIDExIEV1cm9wZWFuIGNvdW50cmllcy4NCg0KVGhpcyBjb2RlIHdhcyBtYWRlIGluICpSc3R1ZGlvKiBhbmQgaGFzIHNvbWUgcXVlcnlzIGZyb20gKipTTFEqKiBpbnRlZ3JhdGVkIGluIG9yZGVyIHRvIHNob3cgbXkgYWJpbGl0aWVzIGluIGJvdGggbGFuZ3VhZ2VzLiBUaGlzIHdvcmtzIGJlY2F1c2UgdGhlIHNvdXJjZSBvZiBkYXRhIGlzIGFuIHNxbCBmaWxlIGJ1dCBpdCBjYW4gYmUgaW1wb3J0ZWQgdG8gUnN0dWRpby4NCg0KRm9yIHRoaXMgcHJvamVjdCwgSSB3YW50IHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9ucyByZWxhdGVkIHRvIHRoZSBkYXRhOiAxLiDCv1doYXQgd2FzIHRoZSBhdmVyYWdlIGFnZSBvZiBmb290YmFsbCBwbGF5ZXJzIGluIEV1cm9wZSBpbiAyMDA4IGFuZCAyMDE2PyAyLiDCv1dobyB3YXMgdGhlIGJlc3QgcGxheWVyIGluIEV1cm9wZSBhY2NvcmRpbmcgdG8gdGhlIG92ZXJhbGwgcmFua2luZz8gMy4gV2hhdCB0ZWFtIHNjb3JlIHRoZSBtb3N0IGdvYWxzIGluIEV1cm9wZSBiZXR3ZWVuIDIwMDggYW5kIDIwMTY/IDQuIMK/V2hhdCB3YXMgdGhlIHRlYW0gdGhhdCBoYWQgdGhlIG1vc3QgcG9pbnRzIGluIEV1cm9wZSBhbmQgd2hhdCB0ZWFtcyB3ZXJlIHRoZSBiZXN0IGhvbWUgb3IgYXdheT8NCg0KIyMgSW1wb3J0IGRhdGENCg0KVGhlIGZpcnN0IHN0ZXAgSSB0YWtlIGluIHRoZSBhbmFseXNpcyBwcm9jZXNzIGlzIHRvIGdldCB0byBrbm93IHRoZSBkYXRhYmFzZS4NCg0KTGV0wrRzIHN0YXJ0IGNoYXJnaW5nIHRoZSBwYWNrYWdlcyB3ZSBuZWVkOg0KDQpgYGB7cn0NCmxpYnJhcnkoUlNRTGl0ZSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQpBZnRlciBkb3dubG9hZGluZyB0aGUgZGF0YSwgd2UgbmVlZCB0byBpbXBvcnQgaXQgdG8gUnN0dWRpby4gRmlyc3QsIHdlIG5lZWQgdG8gZXN0YWJsaXNoIGEgY29ubmVjdGlvbiBiZXR3ZWVuIFJzdHVkaW8gYW5kIHRoZSBTUUwgZGF0YWJhc2UNCg0KYGBge3J9DQpiZF9mb290YmFsbCA8LSBkYkNvbm5lY3QoU1FMaXRlKCksIGRibmFtZT0iZGF0YWJhc2Uuc3FsaXRlIikNCmBgYA0KDQpUaGUgcGFja2FnZSAqUlNRTGl0ZSogaW5jbHVkZXMgZGlmZmVyZW50IGZ1bmN0aW9ucyB0byBtYW5pcHVsYXRlIFNRTCB3aXRoIHF1ZXJ5cyBpbiBSc3R1ZGlvLiBXZSBzZWUgdGhlIG5hbWVzIG9mIHRoZSB0YWJsZXMgdGhhdCB0aGUgZGF0YWJhc2UgaW5jbHVkZXMNCg0KYGBge3IgZWNobz1UUlVFfQ0KYmRfbmFtZXMgPC0gZGJMaXN0VGFibGVzKGJkX2Zvb3RiYWxsKQ0KDQpwcmludChiZF9uYW1lcykNCmBgYA0KDQpXaXRoIHRoYXQgY29kZSB3ZSBjYW4gc2VlIGFsbCBuYW1lcyBpbiB0aGUgZGF0YWJhc2UsIHNvIHdlIGNhbiBzZWxlY3QgdGhlIHRhYmxlICpQbGF5ZXIqIGFuZCBzZWUgbW9yZSBvZiBpdC5XZSBuZWVkIHRvIHVzZSBTUUwgY29kZSB0byBnZXQgdGhlIHRhYmxlIHRvIFJzdHVkaW86DQoNCmBgYHtyIGVjaG89VFJVRX0NCnBsYXllcnMgPC0gI3NhdmUgdGhlIG5hbWUgb2YgdGhlIHRhYmxlDQogIGFzX3RpYmJsZSggI0NvbnZlcnQgdG8gdGliYmxlDQogICAgZGJHZXRRdWVyeShiZF9mb290YmFsbCwgDQogICAgICAgICAgICAgICAiU2VsZWN0KiBmcm9tIFBsYXllciIpKSAlPiUgDQogIGNsZWFuX25hbWVzKCkNCg0KcGxheWVycyAjUHJpbnQgdGhlIHRpYmJsZSBwbGF5ZXJzDQpgYGANCg0KTm93LCB3ZSBjYW4gc3RhcnQgdG8gYW5zd2VyIHRoZSBhYm92ZSBxdWVzdGlvbnMNCg0KIyAxLldoYXQgd2FzIHRoZSBhdmVyYWdlIGFnZSBvZiBmb290YmFsbCBwbGF5ZXJzIGluIEV1cm9wZSBpbiAyMDA4IGFuZCAyMDE2Pw0KDQpXZSBzYXcgdGhhdCB0aGUgdGFibGUgUGxheWVycyBoYWQgdGhlIGluZm9ybWF0aW9uIG5lZWRlZCB0byBhbnN3ZSB0aGlzIGZpcnN0IHF1ZXN0aW9uLiBUaGVyZSBhcmUgdG8gd2F5cyB3ZSBjYW4gZG8gaXQuIFRoZSBmaXJzdCBpcyB3aXRoIFNRTDoNCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsIA0KICAgICAgICAgICAgICAgICAgICAgIlNlbGVjdCANCiAgICAgICAgICAgICAgICAgICAgYXZnKHN0cmZ0aW1lKCclWScsICcyMDA4LTEyLTEyIDAwOjAwOjAwJykgLSANCiAgICAgICAgICAgICAgICAgICAgIHN0cmZ0aW1lKCclWScsIGJpcnRoZGF5KSkgIGFzIGF2ZXJhZ2VfYWdlXzIwMDgsDQogICAgICAgICAgICAgICAgICAgICBhdmcoc3RyZnRpbWUoJyVZJywgJzIwMTYtMTItMTIgMDA6MDA6MDAnKSAtIA0KICAgICAgICAgICAgICAgICAgICAgc3RyZnRpbWUoJyVZJywgYmlydGhkYXkpKSBhcyBhdmVyYWdlX2FnZV8yMDE2DQogICAgICAgICAgICAgICAgICAgICBGcm9tIFBsYXllciIpKQ0KYGBgDQoNClRoYXQgU1FMIHF1ZXJ5IGhlbHBlZCB1cyB0byBleHRyYWN0IHRoZSB5ZWFyIG9mIHRoZSBkYXRlIG9mIGJpcnRoIG9mIGVhY2ggcGxheWVyIGFuZCB0aGVuIHdlIHJlc3QgaXQgdG8gMjAwOCBhbmQgMjAxNiBzbyB3ZSBjb3VsZCBnZXQgdGhlIGF2ZXJhZ2UuIEhvd2V2ZXIsIHdlIGNhbiBhbHNvIGRvIHRoaXMgaW4gUjoNCg0KYGBge3IgZWNobz1UUlVFfQ0KcGxheWVycyAlPiUgDQogIHNlbGVjdChiaXJ0aGRheSkgJT4lIA0KICBtdXRhdGUoYmlydGhkYXkgPSBhc19kYXRlKGJpcnRoZGF5KSwNCiAgICAgICAgIHllYXJfb2ZfYmlydGggPSB5ZWFyKGJpcnRoZGF5KSwNCiAgICAgICAgIGFnZV8yMDA4ID0gMjAwOCAtIHllYXJfb2ZfYmlydGgsDQogICAgICAgICBhZ2VfMjAxNiA9IDIwMTYgLSB5ZWFyX29mX2JpcnRoKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfYWdlXzIwMDggPSBtZWFuKGFnZV8yMDA4KSwNCiAgICAgICAgICAgIGF2Z19hZ2VfMjAxNiA9IG1lYW4oYWdlXzIwMTYpKQ0KYGBgDQoNClNvLCB3ZSBoYXZlIHRoZSBhdmVyYWdlIG9mIGFnZSBidXQgd2UgY2FuIGRvIGEgbGl0dGxlIGJpdCBtb3JlIHRvIGV4cGxvcmUgdGhlIGRhdGEuIEZvciBleGFtcGxlLCBsZXRzIGluY2x1ZGUgYSBncmFwaCBvZiB0aGUgcmVsYXRpb24gb2YgaGVpZ2h0IGFuZCB3ZWlnaHQgb2YgdGhlIHBsYXllcnMuDQoNCmBgYHtyIGVjaG89VFJVRX0NCg0KcGxheWVycyAlPiUgDQogIG11dGF0ZShiaXJ0aGRheSA9IGFzX2RhdGUoYmlydGhkYXkpLA0KICAgICAgICAgeWVhcl9vZl9iaXJ0aCA9IHllYXIoYmlydGhkYXkpLA0KICAgICAgICAgYWdlXzIwMDggPSAyMDA4IC0geWVhcl9vZl9iaXJ0aCkgJT4lICNPYnRhaW4gdGhlIGFnZSBvZiB0aGUgcGxheWVycyBpbiAyMDA4DQogIG11dGF0ZShncm91cF9hZ2UgPSBpZl9lbHNlKGFnZV8yMDA4IDwyMSwgI05ldyB2YXJpYWJsZSB0byBnZXQgdGhlIGdyb3VwIG9mIGFnZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVzcyB0aGFuIDIxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShhZ2VfMjAwOCA8MjYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMZXNzIHRoYW4gMjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoYWdlXzIwMDggPDI2LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMZXNzIHRoYW4gMjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShhZ2VfMjAwOCA8MzAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVzcyB0aGFuIDMwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1vcmUgdGhhbiAzMCIpKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JSANCiAgZ2dwbG90KGFlcyh3ZWlnaHQsIGhlaWdodCwgY29sID0gZ3JvdXBfYWdlKSkrICNTdGFydCB0aGUgZ3JhcGgNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAxLjUpKyAjQWRkIHNvbWUgbm9pc2UgdG8gdGhlIHBvaW50cw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0zKSkpKyAjTWFrZSB0aGUgcG9pbnQgaW4gdGhlIGxlZ2VuZCBiaWdnZXINCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbiBIZWlnaHQgYW5kIFdlaWdodCIsDQogICAgICAgc3VidGl0bGUgPSAiRXVyb3BlYW4gRm9vdGJhbGwgUGxheWVycyBpbiAyMDA4IiwNCiAgICAgICB4ID0gIlcgKExCKSIsDQogICAgICAgeSA9ICJIIChDTSkiLA0KICAgICAgIGNvbCA9ICJBZ2UgR3JvdXAiKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiM0NDAxNTQiLCAjTWFrZSB0aGUgY29sb3Igc2NhbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMzYjUyOGIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMyMTkxOGMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI2ZkZTcyNSIpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMTIwLCAxNTAsIDE3NSwyMDAsIDIyMCwgMjQwKSkrICNNYWtlIHRoZSBicmVha3MNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICNBZGp1c3Qgc29tZSBlbGVtZW50cw0KDQpgYGANCg0KIyAyLiBXaG8gd2FzIHRoZSBiZXN0IHBsYXllciBpbiBFdXJvcGUgYWNjb3JkaW5nIHRvIHRoZSBvdmVyYWxsIHJhbmtpbmc/DQoNCkZpcnN0LCBsZXRzIGdldCB0aGUgZGF0YSB3aXRoIFNRTC4gV2UgY2FuIGdldCBkYXRhIGZyb20gdGhlIHRhYmxlIFBsYXllciBzdWNoIGFzIHRoZSBoZWlnaHQsIHdlaWdodCBhbmQgbmFtZS4gV2l0aCB0aGUgdGFibGUgUGxheWVyX0F0dHJpYnV0ZXMsIHdlIGNhbiBmaW5kIHRoZSBvdmVyYWxsIHJhdGluZyBpbiBldmVyeSBzZWFzb24gc28gd2UgY2FuIGRvIGFuIGF2ZXJhZ2UuIFdpdGggdGhpcyBzaW1wbGUgU1FMIGNvZGUgd2UgZ2V0IHRoZSBkYXRhIGZyb20gYm90aCB0YWJsZXMgYW5kIHRoZW4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggVGlkeXZlcnNlLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQoocmF0aW5nX2hlaWdodCA8LSBhc190aWJibGUoZGJHZXRRdWVyeShiZF9mb290YmFsbCwgDQogICAgICAgICAgICAgICAgICAgICAiU2VsZWN0IGIucGxheWVyX25hbWUsIA0KICAgICAgICAgICAgICAgICAgICAgYi5oZWlnaHQsIA0KICAgICAgICAgICAgICAgICAgICAgYi53ZWlnaHQsDQogICAgICAgICAgICAgICAgICAgICBzdW0oYS5vdmVyYWxsX3JhdGluZykgYXMgb3ZlcmFsbF9yYXRpbmcsIA0KICAgICAgICAgICAgICAgICAgICAgYXZnKG92ZXJhbGxfcmF0aW5nKSBhcyBhdmVyYWdlX3JhdGluZw0KICAgICAgICAgICAgICAgICAgICAgZnJvbSBQbGF5ZXJfQXR0cmlidXRlcyBhcyBhDQogICAgICAgICAgICAgICAgICAgICBMZWZ0IEpvaW4gUGxheWVyIGFzIGINCiAgICAgICAgICAgICAgICAgICAgIE9uIGIucGxheWVyX2FwaV9pZCA9IGEucGxheWVyX2FwaV9pZA0KICAgICAgICAgICAgICAgICAgICAgR3JvdXAgYnkgYS5wbGF5ZXJfYXBpX2lkDQogICAgICAgICAgICAgICAgICAgICBPcmRlciBieSBhdmVyYWdlX3JhdGluZyBkZXNjIikpICU+JSANCiAgbXV0YXRlKHdlaWdodCA9IHdlaWdodCAqIC40NTQsDQogICAgICAgICB3ZWlnaHRfZ3JvdXAgPSBpZl9lbHNlKHdlaWdodDw9NjAsICJMZXNzIHRoYW4gNjBrZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2Uod2VpZ2h0PD03MCwgIkxlc3MgdGhhbiA3MGtnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHdlaWdodCA8PSA4MCwgIkxlc3MgdGhhbiA3NWtnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNb3JlIHRoYW4gODBrZyIpKSkpKQ0KYGBgDQoNCldlIGZvdW5kIHRoYXQgTGlvbmVsIE1lc3NpIHdhcyB0aGUgYmVzdCByYW5rZWQgcGxheWVyIHdpdGggYSBvdmVyYWxsIHJhdGluZyBpbiBFdXJvcGUgYmV0d2VlbiAyMDA4IGFuZCAyMDE2LiBCdXQgd2UgY2FuIHVzZSB0aGUgdGliYmxlIGNyZWF0ZWQgYWJvdmUgdG8gZ3JhcGggdGhlIGRpc3RyaWJ1dGlvbiBvZiByYXRpbmcgYW5kIGhlaWdodCB0byBzZWUgdGhlIHJlbGF0aW9uLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpyYXRpbmdfaGVpZ2h0ICU+JSANCiAgZ2dwbG90KGFlcyhoZWlnaHQsIGF2ZXJhZ2VfcmF0aW5nKSkrDQogIGdlb21faml0dGVyKGFlcyhjb2wgPSB3ZWlnaHRfZ3JvdXApLA0KICAgICAgICAgICAgICBzaXplID0gMi41LA0KICAgICAgICAgICAgICB3aWR0aCA9IDEuNSwNCiAgICAgICAgICAgICAgaGVpZ2h0ID0gMS41KSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSByYXRpbmdfaGVpZ2h0ICU+JSANCiAgICAgICAgICAgICAgZmlsdGVyKGF2ZXJhZ2VfcmF0aW5nID09IG1heChyYXRpbmdfaGVpZ2h0JGF2ZXJhZ2VfcmF0aW5nKXwNCiAgICAgICAgICAgICAgICAgICAgICAgYXZlcmFnZV9yYXRpbmcgPT0gbWF4KHJhdGluZ19oZWlnaHQkYXZlcmFnZV9yYXRpbmdbcmF0aW5nX2hlaWdodCRhdmVyYWdlX3JhdGluZyE9bWF4KHJhdGluZ19oZWlnaHQkYXZlcmFnZV9yYXRpbmcpXSkpLA0KICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBsYXllcl9uYW1lKSwNCiAgICAgICAgICAgIGNoZWNrX292ZXJsYXAgPSBULCANCiAgICAgICAgICAgIG51ZGdlX3ggPSAwLjE2LA0KICAgICAgICAgICAgaGp1c3QgPSAwKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNmZGU3MjUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzIxOTE4YyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzNiNTI4YiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjNDQwMTU0IikpKw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT00KSkpKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgT3ZlcmFsbCBSYW5raW5nIGFuZCBIZWlnaHQgaW4gRXVyb3BlYW4gRm9vdGJhbGxlcnMiLA0KICAgICAgIHN1YnRpdGxlID0gIjIwMDggLSAyMDE2IiwNCiAgICAgICBjb2xvciA9ICJXZWlnaHQgR3JvdXAiLA0KICAgICAgIHggPSAiSGVpZ2h0IiwNCiAgICAgICB5ID0gIkF2ZXJhZ2UgT3ZlcmFsbCBSYXRpbmciKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KIyAzLldoYXQgdGVhbSBzY29yZSB0aGUgbW9zdCBnb2FscyBpbiBFdXJvcGUgYmV0d2VlbiAyMDA4IGFuZCAyMDE2Pw0KDQpMZXRzIGFuc3dlciB0aGlzIHF1ZXN0aW9uIHdpdGggYSBzaW1wbGUgU1FMIGNvZGUuIFdlIG5lZWQgdG8gdXNlIHR3byB0YWJsZXMsIHRoZSBUZWFtIGFuZCBNYXRjaCBzbyB3ZSBjYW4gaGF2ZSB0aGUgY29tcGxldGUgbmFtZSBvZiB0aGUgdGVhbSBhbmQgc3VtIHRoZSBhd2F5IGFuZCBob21lIGdvYWxzIG9mIGVhY2ggdGVhbSBzbyB3ZSBjYW4gZ2V0IHRoZSB0b3Agc2NvcmVycy4NCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsIA0KICAgICAgICAgICAgICAgICAgICAgIlNFTEVDVA0KICAgICAgICAgICBiLnRlYW1fbG9uZ19uYW1lIGFzIHRlYW0sDQogICAgICAgICAgIHN1bShhLmhvbWVfdGVhbV9nb2FsKSsgc3VtKGEuYXdheV90ZWFtX2dvYWwpIGFzIGdvYWxzDQogICAgICAgICAgIEZST00gTWF0Y2ggYXMgYQ0KICAgICAgICAgICBMZWZ0IEpvaW4gVGVhbSBhcyBiIG9uIGIudGVhbV9hcGlfaWQgPSBhLmhvbWVfdGVhbV9hcGlfaWQgDQogICAgICAgICAgIEdyb3VwIGJ5IGIudGVhbV9hcGlfaWQNCiAgICAgICAgICAgT3JkZXIgYnkgZ29hbHMgRGVzYw0KICAgICAgICAgICAiKSkNCmBgYA0KDQpMZXRzIHNlZSB0aGUgZGlzdHJ1dGlvbiBvZiBnb2FscyBzY29yZWQgYnkgZXVyb3BlYW4gdGVhbXM6DQoNClRvIGFuc3dlciB0aGUgbmV4dCBxdWVzdGlvbnMsIGxldHMgc3RhcnQgYnkgY3JlYXRpbmcgYSBuZXcgdGFibGUgd2l0aCB0aGUgZGF0YSB3ZSBuZWVkLiBOb3csIHdlIGtub3cgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0YWJsZXMgTWF0Y2ggYW5kIFRlYW0sIHNvIHdlIGNhbiBnZXQgdGhlIGluZm9ybWF0aW9uIG9mIGVhY2ggbWF0Y2ggcGxheWVkIGluIGV2ZXJ5IHNlYXNvbiBpbiBhbGwgY291bnRyaWVzLiBBbHNvLCBiYXNlZCBvbiB0aGUgZ29hbHMgdGhhdCBlYWNoIHRlYW0gc2NvcmVkIHdlIGNhbiBtYWtlIHRoZSBwb2ludHMgZWFjaCBvbmUgZ290LiBJZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIGZvb3RiYWxsLCB3aGVuIGEgdGVhbSB3aW5zIHRoZXkgZ2V0IDMgcG9pbnRzLCAxIGZvciBhIHRpZSBhbmQgMCBmb3IgbG9zaW5nLiBUaGUgbmV4dCBTUUwgY29kZSBjcmVhdGVzIHRoYXQgdGFibGUgY2FsbGVkIHBvaW50czoNCg0KYGBge3IgZWNobz1UUlVFfQ0KDQoocG9pbnRzIDwtIGFzX3RpYmJsZShkYkdldFF1ZXJ5KGJkX2Zvb3RiYWxsLCANCiAgICAgICAgICAgICAgICAgICAgICJTRUxFQ1QNCiAgICAgICAgICAgYi50ZWFtX2xvbmdfbmFtZSBhcyBob21lX3RlYW0sDQogICAgICAgICAgIGEuaG9tZV90ZWFtX2dvYWwgYXMgaG9tZV9nb2FscywNCiAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID4gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDANCiAgICAgICAgICAgRW5kKSBhcyBob21lX3BvaW50LA0KICAgICAgICAgICBjLnRlYW1fbG9uZ19uYW1lIGFzIGF3YXlfdGVhbSwNCiAgICAgICAgICAgYS5hd2F5X3RlYW1fZ29hbCBhcyBhd2F5X2dvYWxzLA0KICAgICAgICAgICAoQ2FzZQ0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPSBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMQ0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPiBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPCBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMw0KICAgICAgICAgICBFbmQpIGFzIGF3YXlfcG9pbnQsDQogICAgICAgICAgIGEuZGF0ZSwNCiAgICAgICAgICAgYS5zZWFzb24sDQogICAgICAgICAgIGQubmFtZSBhcyBsZWFndWVfbmFtZSwNCiAgICAgICAgICAgZS5uYW1lIGFzIGNvdW50cnkNCiAgICAgICAgICAgRlJPTSBNYXRjaCBhcyBhDQogICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGIgb24gYi50ZWFtX2FwaV9pZCA9IGEuaG9tZV90ZWFtX2FwaV9pZCANCiAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYyBvbiBjLnRlYW1fYXBpX2lkID0gYS5hd2F5X3RlYW1fYXBpX2lkDQogICAgICAgICAgIExlZnQgSm9pbiBMZWFndWUgYXMgZCBvbiBkLmNvdW50cnlfaWQgPSBhLmNvdW50cnlfaWQNCiAgICAgICAgICAgTGVmdCBKb2luIENvdW50cnkgYXMgZSBvbiBlLmlkID0gYS5jb3VudHJ5X2lkDQogICAgICAgICAgICIpKSkgDQpgYGANCg0KIyA0LiBXaGF0IHdhcyB0aGUgdGVhbSB0aGF0IGhhZCB0aGUgbW9zdCBwb2ludHMgaW4gRXVyb3BlIGFuZCB3aGF0IHRlYW1zIHdlcmUgdGhlIGJlc3QgaG9tZSBhbmQgYXdheSB0ZWFtPw0KDQpMZXTCtHMgc2VlIGhvdyBjYW4gd2UgZ2V0IHRoZSBpbmZvcm1hdGlvbiB3aXRoIGEgbW9yZSBjb21wbGV4IFNRTCBxdWVyeSB3aXRoIHN1YnF1ZXJ5cyBhbmQgam9pbnMgdG8gZ2V0IHRoZSB2YXJpYWJsZXMgd2Ugd2FudDoNCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsDQogICAgICAgICAgICAgICAgICAgICAiDQogICAgICAgICAgICAgIFNlbGVjdA0KICAgICAgICAgICAgICAgIGhvbWUuaG9tZV90ZWFtIGFzIHRlYW0sDQogICAgICAgICAgICAgICAgaG9tZS50b3RhbF9ob21lX3BvaW50cyBhcyB0b3RhbF9ob21lX3BvaW50cywNCiAgICAgICAgICAgICAgICBhd2F5LnRvdGFsX2F3YXlfcG9pbnRzIGFzIHRvdGFsX2F3YXlfcG9pbnRzDQogICAgICAgICAgICAgIEZyb20NCiAgICAgICAgICAgICAgICAgICAgICgNCiAgICAgICAgICAgICAgICAgICAgIFNlbGVjdA0KICAgICAgICAgICAgICAgICAgICAgei5pZF9ob21lIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICB6LmhvbWVfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgIHN1bSh6LmhvbWVfcG9pbnQpIGFzIHRvdGFsX2hvbWVfcG9pbnRzDQogICAgICAgICAgICAgICAgICAgICBGcm9tDQogICAgICAgICAgICAgICAgICAgICAoU2VsZWN0DQogICAgICAgICAgICAgICAgICAgICBiLnRlYW1fbG9uZ19uYW1lIGFzIGhvbWVfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgIGMudGVhbV9sb25nX25hbWUgYXMgYXdheV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgYS5ob21lX3RlYW1fYXBpX2lkIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICAoQ2FzZQ0KICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA+IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAzDQogICAgICAgICAgICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPCBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICAgICAgICAgICAgRW5kKSBhcyBob21lX3BvaW50LA0KICAgICAgICAgICAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA9IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAxDQogICAgICAgICAgICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPiBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgICAgICAgICAgIEVuZCkgYXMgYXdheV9wb2ludCwNCiAgICAgICAgICAgICAgICAgICAgIGEuYXdheV90ZWFtX2FwaV9pZCBhcyBpZF9hd2F5DQogICAgICAgICAgICAgICAgICAgICBGUk9NIE1hdGNoIGFzIGENCiAgICAgICAgICAgICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGIgb24gYi50ZWFtX2FwaV9pZCA9IGEuaG9tZV90ZWFtX2FwaV9pZA0KICAgICAgICAgICAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYyBvbiBjLnRlYW1fYXBpX2lkID0gYS5hd2F5X3RlYW1fYXBpX2lkKSBhcyB6DQogICAgICAgICAgICAgICAgICAgICBHcm91cCBieSBpZF9ob21lKSBhcyBob21lDQogICAgICAgICAgICAgIElubmVyIEpvaW4gKA0KICAgICAgICAgICAgICAgICAgICAgICAgICBTZWxlY3QNCiAgICAgICAgICAgICAgICAgICAgICAgICAgei5pZF9hd2F5IGFzIGlkX2F3YXksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHouYXdheV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oei5hd2F5X3BvaW50KSBhcyB0b3RhbF9hd2F5X3BvaW50cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRnJvbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAoU2VsZWN0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGIudGVhbV9sb25nX25hbWUgYXMgaG9tZV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjLnRlYW1fbG9uZ19uYW1lIGFzIGF3YXlfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYS5ob21lX3RlYW1fYXBpX2lkIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIChDYXNlDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA9IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAxDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA+IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAzDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA8IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAwDQogICAgICAgICAgICAgICAgICAgICAgICAgIEVuZCkgYXMgaG9tZV9wb2ludCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID4gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDANCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgRW5kKSBhcyBhd2F5X3BvaW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhLmF3YXlfdGVhbV9hcGlfaWQgYXMgaWRfYXdheQ0KICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIE1hdGNoIGFzIGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYiBvbiBiLnRlYW1fYXBpX2lkID0gYS5ob21lX3RlYW1fYXBpX2lkDQogICAgICAgICAgICAgICAgICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGMgb24gYy50ZWFtX2FwaV9pZCA9IGEuYXdheV90ZWFtX2FwaV9pZCkgYXMgeg0KICAgICAgICAgICAgICAgICAgICAgICAgICBHcm91cCBieSBpZF9hd2F5KSBhcyBhd2F5DQogICAgICAgICAgICAgIE9uIGF3YXkuYXdheV90ZWFtID0gaG9tZS5ob21lX3RlYW0NCiAgICAgICAgICAgICAgT3JkZXIgYnkgdG90YWxfaG9tZV9wb2ludHMgZGVzYyINCiAgDQopKQ0KYGBgDQoNCk5vdywgdGhlIHNhbWUgcHJvY2VzcyBidXQgaW4gVGlkeXZlcnNlIGFuZCBhIGdyYXBoIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YToNCg0KYGBge3IgZWNobz1UUlVFfQ0KcG9pbnRzX2hvbWUgPC0gcG9pbnRzICU+JSANCiAgZ3JvdXBfYnkoaG9tZV90ZWFtKSAlPiUgDQogIHN1bW1hcmlzZShzdW1faG9tZV9wb2ludHMgPSBzdW0oaG9tZV9wb2ludCkpICU+JSANCiAgcmVuYW1lKHRlYW0gPSAiaG9tZV90ZWFtIikgJT4lIA0KICBhcnJhbmdlKC1zdW1faG9tZV9wb2ludHMpICU+JSANCiAgZmlsdGVyKHN1bV9ob21lX3BvaW50cyA+IDMzMCkNCg0KcG9pbnRzX2F3YXkgPC0gcG9pbnRzICU+JSANCiAgZ3JvdXBfYnkoYXdheV90ZWFtKSAlPiUgDQogIHN1bW1hcmlzZShzdW1fYXdheV9wb2ludHMgPSBzdW0oYXdheV9wb2ludCkpICU+JSANCiAgcmVuYW1lKHRlYW0gPSAiYXdheV90ZWFtIikgJT4lIA0KICBhcnJhbmdlKC1zdW1fYXdheV9wb2ludHMpICU+JSANCiAgZmlsdGVyKHN1bV9hd2F5X3BvaW50cyA+MjUwKQ0KDQoNCmhvbWVfYXdheV9wb2ludHMgPC0gaW5uZXJfam9pbihwb2ludHNfaG9tZSwgcG9pbnRzX2F3YXksIGJ5ID0gam9pbl9ieSh0ZWFtKSkgJT4lIA0KICBwaXZvdF9sb25nZXIoLXRlYW0sIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiYXdheV9vcl9ob21lIiwNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwb2ludHMiKSAlPiUNCiAgbXV0YXRlKGF3YXlfb3JfaG9tZSA9IGlmX2Vsc2UoYXdheV9vcl9ob21lID09ICJzdW1faG9tZV9wb2ludHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkhvbWUgcG9pbnRzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJBd2F5IHBvaW50cyIpKSAlPiUgDQogIGdncGxvdChhZXMoZmN0X3Jlb3JkZXIodGVhbSwgcG9pbnRzKSwgcG9pbnRzKSkrDQogIGdlb21fY29sKGFlcyhmaWxsID0gYXdheV9vcl9ob21lKSwgDQogICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAuOSksDQogICAgICAgICAgIGNvbCA9ICJibGFjayIpKw0KICBjb29yZF9mbGlwKCkrDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCA0MjApKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0IxMkE5MEZGIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMEQwODg3RkYiKSkrDQogIGxhYnModGl0bGUgPSAiSG9tZSBhbmQgQXdheSBQb2ludHMgaW4gRXVyb3BlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDA4LTIwMTYiLA0KICAgICAgIGZpbGwgPSAiIiwNCiAgICAgICB4ID0gIlRlYW0iLCANCiAgICAgICB5ID0gIlBvaW50cyIpKw0KICB0aGVtZV9idygpKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLjIsICJjbSIpLA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSkNCg0KZ2dwbG90bHkoaG9tZV9hd2F5X3BvaW50cywgd2lkdGggPSAxMDAwKQ0KDQpgYGANCg==